home *** CD-ROM | disk | FTP | other *** search
/ Visual Cafe 3 / Visual Cafe 3.ISO / Vcafe / JFC.bin / RepaintManager.java < prev    next >
Text File  |  1998-06-30  |  15KB  |  501 lines

  1. /*
  2.  * @(#)RepaintManager.java    1.24 98/06/02
  3.  *
  4.  * Copyright 1997, 1998 by Sun Microsystems, Inc.,
  5.  * 901 San Antonio Road, Palo Alto, California, 94303, U.S.A.
  6.  * All rights reserved.
  7.  *
  8.  * This software is the confidential and proprietary information
  9.  * of Sun Microsystems, Inc. ("Confidential Information").  You
  10.  * shall not disclose such Confidential Information and shall use
  11.  * it only in accordance with the terms of the license agreement
  12.  * you entered into with Sun.
  13.  */
  14. package com.sun.java.swing;
  15.  
  16.  
  17. import java.awt.*;
  18. import java.awt.event.*;
  19. import java.util.*;
  20. import java.applet.*;
  21.  
  22.  
  23. /**
  24.  * @version 1.24 06/02/98
  25.  * @author Arnaud Weber
  26.  */
  27. public class RepaintManager 
  28. {
  29.     Hashtable dirtyComponents;
  30.     Vector    invalidComponents;
  31.     boolean   doubleBufferingEnabled = true;
  32.     Image     doubleBuffer;
  33.     Dimension doubleBufferMaxSize;
  34.  
  35.     private static final Object repaintManagerKey = RepaintManager.class;
  36.  
  37.     /** 
  38.      * Return the RepaintManager for the calling thread.
  39.      */
  40.     public static RepaintManager currentManager(Component c) {
  41.         RepaintManager result = (RepaintManager) SwingUtilities.appContextGet(repaintManagerKey);
  42.         if(result == null) {
  43.             result = new RepaintManager();
  44.             SwingUtilities.appContextPut(repaintManagerKey, result);
  45.         }
  46.     return result;
  47.     }
  48.  
  49.     /**
  50.      * @return currentManager((Component)<b>c</b>)
  51.      */
  52.     public static RepaintManager currentManager(JComponent c) {
  53.     return currentManager((Component)c);
  54.     }
  55.  
  56.  
  57.     /** Set the RepaintManager that should be used for the calling 
  58.      *  thread. <b>aRepaintManager</b> will become the current RepaintManager
  59.      *  for the calling thread's thread group.
  60.      */
  61.     public static void setCurrentManager(RepaintManager aRepaintManager) {
  62.         if (aRepaintManager != null) {
  63.             SwingUtilities.appContextPut(repaintManagerKey, aRepaintManager);
  64.         } else {
  65.             SwingUtilities.appContextRemove(repaintManagerKey);
  66.         }
  67.     }
  68.  
  69.     /** Create a new RepaintManager instance. You rarely call this constructor.
  70.      *  directly. To get the default RepaintManager, use 
  71.      *  RepaintManager.currentManager(JComponent) (normally "this").
  72.      */
  73.     public RepaintManager() {
  74.     dirtyComponents = new Hashtable();
  75.         doubleBufferMaxSize = Toolkit.getDefaultToolkit().getScreenSize();
  76.     }
  77.  
  78.  
  79.     /**
  80.      * Mark the component as in need of layout and queue a runnable
  81.      * for the event dispatching thread that will validate the components
  82.      * first isValidateRoot() ancestor. 
  83.      * 
  84.      * @see JComponent#isValidateRoot
  85.      * @see #removeInvalidComponent
  86.      */
  87.     public synchronized void addInvalidComponent(JComponent invalidComponent) 
  88.     {
  89.         Component validateRoot = null;
  90.  
  91.     /* Find the first JComponent ancestor of this component whose
  92.      * isValidateRoot() method returns true.  
  93.      */
  94.         for(Component c = invalidComponent; c != null; c = c.getParent()) {
  95.         if ((c instanceof CellRendererPane) || (c.getPeer() == null)) {
  96.         return;
  97.         }
  98.         if ((c instanceof JComponent) && (((JComponent)c).isValidateRoot())) {
  99.         validateRoot = c;
  100.         break;
  101.         }
  102.     }
  103.         
  104.     /* There's no validateRoot to apply validate to, so we're done.
  105.      */
  106.     if (validateRoot == null) {
  107.         return;
  108.     }
  109.  
  110.     /* If the validateRoot and all of its ancestors aren't visible
  111.      * then we don't do anything.  While we're walking up the tree
  112.      * we find the root Window or Applet.
  113.      */
  114.     Component root = null;
  115.     
  116.     for(Component c = validateRoot; c != null; c = c.getParent()) {
  117.         if (!c.isVisible() || (c.getPeer() == null)) {
  118.         return;
  119.         }
  120.         if ((c instanceof Window) || (c instanceof Applet)) {
  121.         root = c;
  122.         break;
  123.         }
  124.     }
  125.  
  126.     if (root == null) {
  127.         return;
  128.     }
  129.        
  130.     /* Lazily create the invalidateComponents vector and add the
  131.      * validateRoot if it's not there already.  If this validateRoot
  132.      * is already in the vector, we're done.
  133.      */
  134.     if (invalidComponents == null) {
  135.         invalidComponents = new Vector();
  136.     }
  137.     else {
  138.         int n = invalidComponents.size();
  139.         for(int i = 0; i < n; i++) {
  140.         if(validateRoot == (Component)(invalidComponents.elementAt(i))) {
  141.             return;
  142.         }
  143.         }
  144.     }
  145.     invalidComponents.addElement(validateRoot);
  146.  
  147.     /* Queues a Runnable that calls RepaintManager.validateInvalidComponents() 
  148.      * and RepaintManager.paintDirtyRegions() with SwingUtilities.invokeLater().
  149.      */
  150.     SystemEventQueueUtilities.queueComponentWorkRequest(root);
  151.     }
  152.  
  153.  
  154.     /** 
  155.      * Remove a component from the list of invalid components.
  156.      * 
  157.      * @see #addInvalidComponent
  158.      */
  159.     public synchronized void removeInvalidComponent(JComponent component) {
  160.         if(invalidComponents != null) {
  161.             int index = invalidComponents.indexOf(component);
  162.             if(index != -1) {
  163.                 invalidComponents.removeElementAt(index);
  164.             }
  165.         }
  166.     }
  167.  
  168.  
  169.     /** 
  170.      * Add a component in the list of components that should be refreshed.
  171.      * If <i>c</i> already has a dirty region, the rectangle <i>(x,y,w,h)</i> 
  172.      * will be unioned with the region that should be redrawn. 
  173.      * 
  174.      * @see JComponent#repaint
  175.      */
  176.     public synchronized void addDirtyRegion(JComponent c, int x, int y, int w, int h) 
  177.     {
  178.     /* Special cases we don't have to bother with.
  179.      */
  180.         if ((w <= 0) || (h <= 0) || (c == null)) {
  181.             return;
  182.         }
  183.  
  184.     if ((c.getWidth() <= 0) || (c.getHeight() <= 0)) {
  185.         return;
  186.     }
  187.  
  188.     /* Make sure that c and all it ancestors (up to an Applet or
  189.      * Window) are visible.  This loop has the same effect as 
  190.      * checking c.isShowing() (and note that it's still possible 
  191.      * that c is completely obscured by an opaque ancestor in 
  192.      * the specified rectangle).
  193.      */
  194.     Component root = null;
  195.  
  196.     for (Container p = c; p != null; p = p.getParent()) {
  197.         if (!p.isVisible() || (p.getPeer() == null)) {
  198.         return;
  199.         }
  200.         if ((p instanceof Window) || (p instanceof Applet)) {
  201.         root = p;
  202.         break;
  203.         }
  204.     }
  205.  
  206.     /* Lazily create the dirtyComponents Hashtable and add
  207.      * x,y,w,h to the entry for c.
  208.      */
  209.     if(dirtyComponents == null) {
  210.         dirtyComponents = new Hashtable();
  211.         dirtyComponents.put(c, new Rectangle(x, y, w, h));
  212.     }
  213.     else {
  214.         Rectangle r = (Rectangle)dirtyComponents.get(c);
  215.         if (r == null) {
  216.         dirtyComponents.put(c, new Rectangle(x, y, w, h));
  217.         }
  218.         else {
  219.         SwingUtilities.computeUnion(x, y, w, h, r);
  220.         }
  221.     }
  222.  
  223.     /* Queues a Runnable that calls validateInvalidComponents() and
  224.      * rm.paintDirtyRegions() with SwingUtilities.invokeLater().
  225.      */
  226.     SystemEventQueueUtilities.queueComponentWorkRequest(root);
  227.     }
  228.     
  229.  
  230.     /** Return the current dirty region for a component.
  231.      *  Return an empty rectangle if the component is not
  232.      *  dirty.
  233.      */
  234.     public Rectangle getDirtyRegion(JComponent aComponent) {
  235.     Rectangle r = null;
  236.     synchronized(this) {
  237.         if(dirtyComponents != null)
  238.         r = (Rectangle)dirtyComponents.get(aComponent);
  239.     }
  240.     if(r == null)
  241.         return new Rectangle(0,0,0,0);
  242.     else
  243.         return new Rectangle(r);
  244.     }
  245.  
  246.     /** Mark a component completely dirty. <b>aComponent</b> will be
  247.      *  completely painted during the next paintDirtyRegions() call.
  248.      */
  249.     public void markCompletelyDirty(JComponent aComponent) {
  250.     addDirtyRegion(aComponent,0,0,Integer.MAX_VALUE,Integer.MAX_VALUE);
  251.     }
  252.         
  253.     /** Mark a component completely clean. <b>aComponent</b> will not
  254.      *  get painted during the next paintDirtyRegions() call
  255.      */
  256.     public void markCompletelyClean(JComponent aComponent) {
  257.     synchronized(this) {
  258.         if(dirtyComponents != null)
  259.         dirtyComponents.remove(aComponent);
  260.     }
  261.     }
  262.  
  263.     /** Convenience that returns true if <b>aComponent</b> will be completely
  264.      *  painted during the next paintDirtyRegions(). If computing dirty regions is
  265.      *  expensive for your component, use this method and avoid computing dirty region
  266.      *  if it return true.
  267.      */
  268.     public boolean isCompletelyDirty(JComponent aComponent) {
  269.     Rectangle r;
  270.     
  271.     r = getDirtyRegion(aComponent);
  272.     if(r.width == Integer.MAX_VALUE &&
  273.        r.height == Integer.MAX_VALUE)
  274.         return true;
  275.     else
  276.         return false;
  277.     }
  278.  
  279.  
  280.     /** 
  281.      * Validate all of the components that have been marked invalid.
  282.      * @see #addInvalidComponent
  283.      */
  284.     public void validateInvalidComponents() {
  285.         Vector ic;
  286.         synchronized(this) {
  287.             if(invalidComponents == null) {
  288.                 return;
  289.         }
  290.             ic = invalidComponents;
  291.             invalidComponents = null;
  292.         }
  293.     int n = ic.size();
  294.         for(int i = 0; i < n; i++) {
  295.             ((Component)ic.elementAt(i)).validate();
  296.         }
  297.     }
  298.     
  299.  
  300.     /**
  301.      * Paint all of the components that have been marked dirty.
  302.      * 
  303.      * @see #addDirtyRegion
  304.      */
  305.     public void paintDirtyRegions() {
  306.         int i, count;
  307.     Vector roots;
  308.         JComponent dirtyComponent;
  309.  
  310.         Hashtable tmpDirtyComponents;
  311.     synchronized(this) {
  312.         if(dirtyComponents == null) {
  313.         return;
  314.             }
  315.         tmpDirtyComponents = dirtyComponents;
  316.         dirtyComponents = null;
  317.     }
  318.  
  319.         count = tmpDirtyComponents.size();
  320.         if (count == 0) {
  321.             return;
  322.         } 
  323.  
  324.         Rectangle rect;
  325.         Rectangle localBounds;
  326.         Enumeration keys;
  327.  
  328.         roots = new Vector(count);
  329.         keys = tmpDirtyComponents.keys();
  330.  
  331.         while(keys.hasMoreElements()) {
  332.             dirtyComponent = (JComponent) keys.nextElement();
  333.             collectDirtyComponents(tmpDirtyComponents, dirtyComponent, roots);
  334.         }
  335.  
  336.         count = roots.size();
  337.         //        System.out.println("roots size is " + count);
  338.         for(i=0 ; i < count ; i++) {
  339.             dirtyComponent = (JComponent) roots.elementAt(i);
  340.             rect = (Rectangle) tmpDirtyComponents.get(dirtyComponent);
  341.             //            System.out.println("Should refresh :" + rect);
  342.             localBounds = dirtyComponent.getBounds();
  343.             localBounds.x = localBounds.y = 0;
  344.             rect = rect.intersection(localBounds);
  345.             // System.out.println("** paint of " + dirtyComponent + rect);
  346.             dirtyComponent.paintImmediately(rect.x,rect.y,rect.width,rect.height);
  347.         }
  348.     }
  349.  
  350.  
  351.  
  352.     void collectDirtyComponents(Hashtable dirtyComponents,
  353.                 JComponent dirtyComponent,
  354.                 Vector roots) {
  355.         int dx, dy, rootDx, rootDy;
  356.         Component component, rootDirtyComponent,parent;
  357.     Rectangle tmp;
  358.         Rectangle cBounds;
  359.         boolean opaqueAncestorFound = false;
  360.  
  361.         // Find the highest parent which is dirty.  When we get out of this
  362.         // rootDx and rootDy will contain the translation from the
  363.         // rootDirtyComponent's coordinate system to the coordinates of the
  364.         // original dirty component.  The tmp Rect is also used to compute the
  365.         // visible portion of the dirtyRect.
  366.  
  367.         component = rootDirtyComponent = dirtyComponent;
  368.  
  369.         cBounds = dirtyComponent._bounds;
  370.  
  371.         dx = rootDx = 0;
  372.         dy = rootDy = 0;
  373.         tmp = new Rectangle((Rectangle) dirtyComponents.get(dirtyComponent));
  374.  
  375.         // System.out.println("Collect dirty component for bound " + tmp + 
  376.         //                                   "component bounds is " + cBounds);;
  377.         SwingUtilities.computeIntersection(0,0,cBounds.width,cBounds.height,tmp);
  378.  
  379.         if (tmp.isEmpty()) {
  380.             // System.out.println("Empty 1");
  381.             return;
  382.         } 
  383.  
  384.         if(dirtyComponent.isOpaque())
  385.             opaqueAncestorFound = true;
  386.  
  387.         for(;;) {
  388.             parent = component.getParent();
  389.             if(parent == null) 
  390.                 break;
  391.  
  392.             if(!(parent instanceof JComponent))
  393.                 break;
  394.  
  395.             component = parent;
  396.  
  397.             if(((JComponent)component).isOpaque())
  398.                 opaqueAncestorFound = true;
  399.  
  400.             dx += cBounds.x;
  401.             dy += cBounds.y;
  402.             tmp.setLocation(tmp.x + cBounds.x,
  403.                             tmp.y + cBounds.y);
  404.  
  405.             cBounds = ((JComponent)component)._bounds;
  406.             tmp = SwingUtilities.computeIntersection(0,0,cBounds.width,cBounds.height,tmp);
  407.  
  408.             if (tmp.isEmpty()) {
  409.                 // System.out.println("Empty 2");
  410.                 return;
  411.             }
  412.  
  413.             if (dirtyComponents.get(component) != null) {
  414.                 rootDirtyComponent = component;
  415.                 rootDx = dx;
  416.                 rootDy = dy;
  417.             }
  418.         } 
  419.  
  420.         if (dirtyComponent != rootDirtyComponent) {
  421.         Rectangle r;
  422.             tmp.setLocation(tmp.x + rootDx - dx,
  423.                 tmp.y + rootDy - dy);
  424.         r = (Rectangle)dirtyComponents.get(rootDirtyComponent);
  425.         SwingUtilities.computeUnion(tmp.x,tmp.y,tmp.width,tmp.height,r);
  426.         }
  427.  
  428.         // If we haven't seen this root before, then we need to add it to the
  429.         // list of root dirty Views.
  430.  
  431.         if (!roots.contains(rootDirtyComponent)) 
  432.             roots.addElement(rootDirtyComponent);    
  433.     }
  434.  
  435.  
  436.     public synchronized String toString() {
  437.     StringBuffer sb = new StringBuffer();
  438.     if(dirtyComponents != null) 
  439.         sb.append("" + dirtyComponents);
  440.         return sb.toString();
  441.     }
  442.  
  443.     /** Return the offscreen buffer that should be used as a double buffer with the component <code>c</code>
  444.      *  By default there is a double buffer per RepaintManager.
  445.      *  The buffer might be smaller than <code>(proposedWidth,proposedHeight)</code>
  446.      *  This happens when the maximum double buffer size as been set for the receiving
  447.      *  repaint manager.
  448.      */
  449.     public Image getOffscreenBuffer(Component c,int proposedWidth,int proposedHeight) {
  450.         Image result;
  451.         int width,height;
  452.  
  453.         if(proposedWidth > doubleBufferMaxSize.width)
  454.             width = doubleBufferMaxSize.width;
  455.         else
  456.             width = proposedWidth;
  457.  
  458.         if(proposedHeight > doubleBufferMaxSize.height) 
  459.             height =doubleBufferMaxSize.height;
  460.         else
  461.             height = proposedHeight;
  462.  
  463.         if(doubleBuffer != null) {
  464.             if(doubleBuffer.getWidth(null) < width ||
  465.                doubleBuffer.getHeight(null) < height)
  466.                 doubleBuffer = null;
  467.         }
  468.  
  469.         if(doubleBuffer == null) {
  470.             doubleBuffer = c.createImage(width,height);
  471.             // System.out.println("Creating offscreen buffer " + new Dimension(width,height));
  472.         }
  473.         return doubleBuffer;
  474.     }
  475.  
  476.     /** Set the maximum double buffer size. **/
  477.     public void setDoubleBufferMaximumSize(Dimension d) {
  478.         doubleBufferMaxSize = d;
  479.         if(doubleBuffer != null) {
  480.             if(doubleBuffer.getWidth(null) > d.width ||
  481.                doubleBuffer.getHeight(null) > d.height)
  482.                 doubleBuffer = null;
  483.         }
  484.     }
  485.  
  486.     public Dimension getDoubleBufferMaximumSize() {
  487.         return doubleBufferMaxSize;
  488.     }
  489.  
  490.     public void setDoubleBufferingEnabled(boolean aFlag) {
  491.         doubleBufferingEnabled = aFlag;
  492.     if(!doubleBufferingEnabled) {
  493.       doubleBuffer = null;
  494.     }
  495.     }
  496.  
  497.     public boolean isDoubleBufferingEnabled() {
  498.       return doubleBufferingEnabled;
  499.     }
  500. }
  501.